package com.shengqin.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.crypto.digest.MD5;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shengqin.annotations.OperaLog;
import com.shengqin.common.pojo.CommonPage;
import com.shengqin.common.pojo.CommonResult;
import com.shengqin.config.CommonException;
import com.shengqin.config.model.TestProjectConfig;
import com.shengqin.entity.*;
import com.shengqin.enums.BusinessType;
import com.shengqin.listeners.PlanFreeTestListener;
import com.shengqin.mapper.PlanMapper;
import com.shengqin.pojo.plan.*;
import com.shengqin.pojo.plan.export.ExportClassesModel;
import com.shengqin.pojo.plan.export.ExportStudentModel;
import com.shengqin.pojo.plan.export.ExportTestEnvModel;
import com.shengqin.pojo.plan.export.ExportTestResultModel;
import com.shengqin.pojo.plan.summary.ChartDataModel;
import com.shengqin.pojo.plan.summary.SummaryTotalProcessModel;
import com.shengqin.pojo.school.TreeModel;
import com.shengqin.pojo.summary.DocParam;
import com.shengqin.pojo.summary.SummaryQueryParam;
import com.shengqin.service.*;
import com.shengqin.utils.*;
import freemarker.template.TemplateException;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static com.shengqin.utils.BmiUtil.buildLevel;

/**
 * <p>
 * 体测计划 前端控制器
 * </p>
 *
 * @author Rui.Zhou
 * @since 2023-12-05
 */
@RestController
@RequestMapping("/admin/v1/plan")
@Slf4j
public class PlanController {

    @Value("${upload.path}")
    private String uploadPath;

    private final DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
    @Resource
    private ISchoolService schoolService;
    @Resource
    private IPlanService planService;
    @Resource
    private IPlanStudentService planStudentService;
    @Resource
    private IPlanFreeTestService planFreeTestService;
    @Resource
    private IClassesService classesService;
    @Resource
    private IStudentService studentService;
    @Resource
    private IPlanRelationClassesService planRelationClassesService;
    @Resource
    private ISummaryResultService summaryResultService;
    @Resource
    private IUserService userService;
    @Resource
    private ITestResultService testResultService;
    @Resource
    private IPlanApplyProjectService applyProjectService;
    @Resource
    private SysHealthProjectService healthProjectService;
    @Resource
    private ISummaryArchiveService summaryResultHisService;
    @Resource
    private TestProjectConfig projectConfig;
    @Resource
    private IStandardService standardService;
    @Resource
    private PlanMapper planMapper;

    private final List<String> heightWeightHeads = new ArrayList<>();

    @PostConstruct
    public void init() {

        heightWeightHeads.add("身高");
        heightWeightHeads.add("体重");
        heightWeightHeads.add("BMI");
    }

    @OperaLog(title = "提交免测学生", businessType = BusinessType.INSERT)
    @PostMapping("saveFreeTest")
    public CommonResult<Boolean> saveFreeTest(@RequestBody PlanStudent planStudent) {
        Plan oldPlan = planService.getById(planStudent.getPlanId());
        if (oldPlan == null) {
            throw new CommonException("该计划不存在！");
        }
        PlanStudent student = planStudentService.queryByPlanIdAndStudyCode(planStudent.getStudyCode(), oldPlan.getId());
        if (student == null) {
            throw new CommonException("该考生信息还未同步至考生管理里面，请确认！");
        }
        planFreeTestService.deleteByPlanIdAndStudyCode(oldPlan.getId(), student.getStudyCode());
        if (!CollectionUtils.isEmpty(planStudent.getFreeTestPrjNameList())) {
            List<PlanFreeTest> freeTestList = new ArrayList<>();
            planStudent.getFreeTestPrjNameList().forEach(freeTestPrjNameModel -> {
                if (StringUtils.hasLength(freeTestPrjNameModel.getReason())) {
                    PlanFreeTest freeTest = new PlanFreeTest();
                    freeTest.setStudyCode(student.getStudyCode());
                    freeTest.setProjectName(freeTestPrjNameModel.getName());
                    freeTest.setReason(freeTestPrjNameModel.getReason());
                    freeTest.setScoreText(freeTestPrjNameModel.getScore());
                    freeTestList.add(freeTest);
                }
            });
            if (!CollectionUtils.isEmpty(freeTestList)) {
                planFreeTestService.importBatchFreeTest(freeTestList, oldPlan.getId());
            }
        }
        return CommonResult.data(true);
    }

    @OperaLog(title = "提交免测学生", businessType = BusinessType.INSERT)
    @PostMapping("saveFreeTestForNoScore")
    public CommonResult<Boolean> saveFreeTestForNoScore(@RequestBody FreeTestImportParam freeTestImportParam) {
        Plan oldPlan = planService.getById(freeTestImportParam.getPlanId());
        if (oldPlan == null) {
            throw new CommonException("该计划不存在！");
        }
        if (CollectionUtils.isEmpty(freeTestImportParam.getStudyCodeList()) && StringUtils.hasLength(freeTestImportParam.getStudyCodes())) {
            freeTestImportParam.setStudyCodes(freeTestImportParam.getStudyCodes().replace("，", ","));
            freeTestImportParam.setStudyCodeList(Arrays.asList(freeTestImportParam.getStudyCodes().split(",")));
        }
        if (CollectionUtils.isEmpty(freeTestImportParam.getStudyCodeList())) {
            throw new CommonException("请输入学生信息！");
        }
        List<PlanStudent> students = planStudentService.queryByPlanIdAndStudyCodes(oldPlan.getId(), freeTestImportParam.getStudyCodeList());
        if (CollectionUtils.isEmpty(students)) {
            throw new CommonException("该考生信息还未同步至考生管理里面，请确认！");
        }
        students.forEach(planStudent -> {
            String studyCode = planStudent.getStudyCode().trim();
            planFreeTestService.deleteByPlanIdAndStudyCode(oldPlan.getId(), studyCode);
            String projectNames = projectConfig.getProjectMap().get(planStudent.getGradeId() + "_" + planStudent.getSex());
            List<String> projectNameList = Arrays.asList(projectNames.split(","));
            if (!CollectionUtils.isEmpty(projectNameList)) {
                List<PlanFreeTest> freeTestList = new ArrayList<>();
                projectNameList.forEach(prjName -> {
                    PlanFreeTest freeTest = new PlanFreeTest();
                    freeTest.setStudyCode(studyCode);
                    freeTest.setProjectName(prjName);
                    freeTest.setReason("免考");
                    freeTest.setScoreText("9");
                    freeTest.setSex(planStudent.getSex());
                    freeTestList.add(freeTest);
                });
                if (!CollectionUtils.isEmpty(freeTestList)) {
                    planFreeTestService.importBatchFreeTest(freeTestList, oldPlan.getId());
                }
            }
        });
        return CommonResult.data(true);
    }

    @PostMapping("queryStudentInfoByStudyCode")
    public CommonResult<PlanStudent> queryStudentInfoByStudyCode(@RequestBody PlanStudent planStudent) {
        Plan oldPlan = planService.getById(planStudent.getPlanId());
        if (oldPlan == null) {
            throw new CommonException("该计划不存在！");
        }
        Set<String> testPrjNames = new HashSet<>();
        PlanStudent student = planStudentService.queryByPlanIdAndStudyCode(planStudent.getStudyCode(), oldPlan.getId());
        if (student == null) {
            throw new CommonException("该考生信息还未同步至考生管理里面，请确认！");
        }
        if (oldPlan.getExamFlag()) {
            PlanApplyProject applyProject = applyProjectService.queryByPlanIdAndStudyCode(oldPlan.getId(), planStudent.getStudyCode());
            if (applyProject == null) {
                throw new CommonException("该考生信息还未报名报项，请确认！");
            }
            if (StringUtils.hasLength(applyProject.getProjectNames())) {
                testPrjNames = new HashSet<>(Arrays.asList(applyProject.getProjectNames().split(",")));
            }
        } else {
            List<Standard> standardList = standardService.listStandardPrjByTemplateId(oldPlan.getTemplateId(), student.getSex(), student.getGradeId());
            if (!CollectionUtils.isEmpty(standardList)) {
                testPrjNames = standardList.stream().map(Standard::getProjectName).collect(Collectors.toSet());
            }
        }
        if (student.getSchoolId() != null) {
            School school = schoolService.getById(student.getSchoolId());
            student.setSchoolName(school.getName());
        }
        if (!CollectionUtils.isEmpty(testPrjNames)) {
            List<FreeTestPrjNameModel> freeTestPrjNameModelList = new ArrayList<>();
            List<PlanFreeTest> freeTestApplies = planFreeTestService.listByPlanIdAndStudyCode(oldPlan.getId(), student.getStudyCode());
            Map<String, PlanFreeTest> freeTestMap = freeTestApplies.stream().collect(Collectors.toMap(PlanFreeTest::getProjectName, f -> f));
            testPrjNames.forEach(testPrjName -> {
                PlanFreeTest oldFreeTest = freeTestMap.getOrDefault(testPrjName, null);
                FreeTestPrjNameModel model = new FreeTestPrjNameModel();
                model.setName(testPrjName);
                if (oldFreeTest != null) {
                    model.setReason(oldFreeTest.getReason());
                    model.setScore(String.valueOf(oldFreeTest.getScore()));
                }
                freeTestPrjNameModelList.add(model);
            });
            student.setFreeTestPrjNameList(freeTestPrjNameModelList);
        }
        student.setTestPrjNames(testPrjNames);
        return CommonResult.data(student);
    }

    @PostMapping("updateStudentInfo")
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Boolean> updateStudentInfo(@RequestBody Plan plan) {
        Plan oldPlan = planService.getById(plan.getId());
        if (oldPlan == null) {
            throw new CommonException("该计划不存在！");
        }
        planStudentService.updateStudentInfo(plan.getId());
        testResultService.updateStudentInfo(plan.getId());
        summaryResultService.updateStudentInfo(plan.getId());
        applyProjectService.updateStudentInfo(plan.getId());
        return CommonResult.data(true);
    }

    @PostMapping("updateCameraIp")
    public CommonResult<Boolean> updateCameraIp(@RequestBody Plan plan) {
        Plan oldPlan = planService.getById(plan.getId());
        if (oldPlan == null) {
            throw new CommonException("该计划不���在！");
        }
        oldPlan.setCameraIp(plan.getCameraIp());
        return CommonResult.data(planService.updateById(oldPlan));
    }

    @PostMapping("queryProjectList")
    public CommonResult<List<String>> queryProjectList(@RequestBody PlanQueryParam planQueryParam) {
        Plan plan = planService.getById(planQueryParam.getPlanId());
        List<SysHealthProject> healthProjects = healthProjectService.listByTemplateId(plan.getTemplateId());

        List<String> heightAndWeightHeadList = healthProjects.stream().map(SysHealthProject::getProjectName).filter(s -> s.equals("身高") || s.equals("体重") || s.equalsIgnoreCase("BMI")).collect(Collectors.toList());
        List<String> headers = new ArrayList<>();
        if (!CollectionUtils.isEmpty(heightAndWeightHeadList)) {
            for (String projectName : heightWeightHeads) {
                if (heightAndWeightHeadList.contains(projectName)) {
                    headers.add(projectName);
                }
            }
        }
        Set<String> otherProjectNames = healthProjects.stream().map(SysHealthProject::getProjectName).filter(s -> !s.equals("身高") && !s.equals("体重") && !s.equalsIgnoreCase("BMI")).collect(Collectors.toSet());
        headers.addAll(otherProjectNames);
        return CommonResult.data(headers);
    }

    @PostMapping("queryPlanStdByStudyCodes")
    public CommonResult<List<PlanStudent>> queryPlanStdByStudyCodes(@RequestBody PlanQueryParam planQueryParam) {
        return CommonResult.data(planStudentService.queryByPlanIdAndStudyCodes(planQueryParam.getPlanId(), planQueryParam.getStudyCodes()));
    }

    @PostMapping("detailById")
    public CommonResult<Plan> detailById(@RequestBody PlanQueryParam planQueryParam) {
        return CommonResult.data(planService.queryDetailById(planQueryParam.getPlanId()));
    }

    @PostMapping("setState")
    public CommonResult<Boolean> setState(@RequestBody PlanSetStateParam planSetStateParam) {
        Plan plan = planService.getById(planSetStateParam.getPlanId());
        User u = userService.getById(planSetStateParam.getUserId());
        String password = MD5.create().digestHex(planSetStateParam.getPassword());
        if (!u.getPassword().equals(password)) {
            throw new CommonException("账号密码错误");
        }
        if (planSetStateParam.getOptionType().equals("end")) {
            plan.setState(3);
            return CommonResult.data(planService.updateById(plan));
        } else if (planSetStateParam.getOptionType().equals("delete")) {
            planStudentService.removeByPlanId(plan.getId());
            planRelationClassesService.removeByPlanId(plan.getId());
            return CommonResult.data(planService.removeById(plan.getId()));
        }
        return CommonResult.data(false);
    }

    @PostMapping("summaryData")
    public CommonResult<Object> summaryData(@RequestBody PlanQueryParam planQueryParam) {
//        Map<String, Object> dataMap = new HashMap<>();
//        Plan plan = planService.getById(planQueryParam.getPlanId());
//        long countStudent = planStudentService.countByPlanId(planQueryParam);
        List<SummaryResult> summaryResultList = summaryResultService.listByPlanId(planQueryParam);
//
//        List<PlanApplyProject> planApplyProjects = applyProjectService.listByPlanId(planQueryParam.getPlanId());
//
//        AtomicReference<Integer> totalTestEndCount = new AtomicReference<>(0);
//        AtomicReference<Integer> totalTestPrjCount = new AtomicReference<>(0);
//        AtomicReference<Integer> testEndPrjCount = new AtomicReference<>(0);
//
//        planApplyProjects.forEach(planApplyProject -> {
//            if (StringUtils.hasLength(planApplyProject.getProjectNames())) {
//                String[] prjNames = planApplyProject.getProjectNames().split(",");
//                totalTestPrjCount.set(totalTestPrjCount.get() + prjNames.length);
//            }
//        });
//
        List<ChartDataModel> bmiLevelList = new ArrayList<>();
        Map<String, Long> bmiLevelMap = new HashMap<>();
        summaryResultList.forEach(summaryResult -> {
            buildLevel(summaryResult, bmiLevelMap);
        });
        bmiLevelMap.forEach((key, value) -> {
            ChartDataModel chartDataModel = new ChartDataModel();
            chartDataModel.setName(key);
            chartDataModel.setValue(value);
            bmiLevelList.add(chartDataModel);
        });
//        dataMap.put("totalCount", countStudent);
//        dataMap.put("testingCount", summaryResultList.size());
//        dataMap.put("testEndCount", totalTestEndCount.get());
//        dataMap.put("bmiLevelData", bmiLevelList);
//        if (!summaryResultList.isEmpty()) {
//            BigDecimal roundedNum = BigDecimal.valueOf(0);
//            if (countStudent > 0) {
//                BigDecimal rate = new BigDecimal(totalTestEndCount.get().toString()).divide(new BigDecimal(countStudent + ""), 2, RoundingMode.DOWN);
//                roundedNum = rate.multiply(new BigDecimal("100"));
//            }
//            dataMap.put("completionRate", roundedNum);
//
//            BigDecimal prjCompletionNum = BigDecimal.valueOf(0);
//            if (totalTestPrjCount.get() > 0) {
//                BigDecimal prjCompletionRate = new BigDecimal(testEndPrjCount.get().toString()).divide(new BigDecimal(totalTestPrjCount.get().toString()), 2, RoundingMode.DOWN);
//                prjCompletionNum = prjCompletionRate.multiply(new BigDecimal("100"));
//            }
//
//            dataMap.put("prjCompletionRate", prjCompletionNum);
//        } else {
//            dataMap.put("completionRate", 0.00);
//            dataMap.put("prjCompletionRate", 0.00);
//        }
        SummaryQueryParam queryParam = new SummaryQueryParam();
        queryParam.setPlanId(planQueryParam.getPlanId());
        SummaryTotalProcessModel processModel = planMapper.querySummaryDataForHealthPlan(queryParam);
        processModel.setBmiLevelData(bmiLevelList);
        return CommonResult.data(processModel);
    }

    @PostMapping("archive")
    public CommonResult<Boolean> summaryArchived(@RequestBody DocParam docParam) {
        PlanQueryParam planQueryParam = new PlanQueryParam();
        planQueryParam.setPlanId(docParam.getPlanId());
        List<SummaryResult> summaryResultList = summaryResultService.listByPlanId(planQueryParam);
        return CommonResult.data(summaryResultHisService.summaryArchived(summaryResultList, docParam.getSchoolYear()));
    }

    @PostMapping("page")
    public CommonResult<Page<Plan>> getPage(@RequestBody CommonPage<Plan> pageParam) {
        return CommonResult.data(planService.getPage(pageParam));
    }

    @PostMapping("cancelFreeTest")
    public CommonResult<Boolean> cancelFreeTest(@RequestBody PlanStudentParam planStudentParam) {
        PlanStudent planStudent = planStudentService.getById(planStudentParam.getId());
        planStudent.setFreeTestFlag(false);
        planStudent.setFreeTestReason("");
        return CommonResult.data(planStudentService.updateById(planStudent));
    }

    @PostMapping("deleteStudent")
    public CommonResult<Boolean> deleteStudent(@RequestBody PlanStudentParam planStudentParam) {
        PlanStudent planStudent = planStudentService.getById(planStudentParam.getId());
        Flowable.just(planStudentParam.getId()).subscribe(this::calculatePlanData);
        return CommonResult.data(planStudentService.removeById(planStudent.getId()));
    }

    @PostMapping("importFreeTestData")
    public CommonResult importFreeTestData(@RequestPart("file") MultipartFile file, @RequestParam Integer planId) {
        try {
            EasyExcel.read(file.getInputStream(), PlanFreeTest.class, new PlanFreeTestListener(planFreeTestService, planId)).sheet().doRead();
        } catch (ExcelDataConvertException e) {
            e.printStackTrace();
            String message = String.format("第%s行，第%s列解析异常，请修改后重新上传", e.getRowIndex() + 1, e.getColumnIndex() + 1);
            return CommonResult.error(message);
        } catch (Exception e) {
            return CommonResult.error("上传失败，请重试。错误信息：" + e.getMessage());
        }
        return CommonResult.ok().setMsg("导入成功");
    }

    @PostMapping("getSchoolList")
    public CommonResult<List<School>> getSchoolList(@RequestBody PlanQueryParam planQueryParam) {
        List<PlanRelationClasses> schoolList = planRelationClassesService.listByPlanId(planQueryParam.getPlanId());
        if (CollectionUtils.isEmpty(schoolList)) {
            return CommonResult.data(new ArrayList<>());
        }
        Set<Integer> schoolIds = new HashSet<>();
        schoolList.forEach(planRelationClasses -> schoolIds.add(planRelationClasses.getShcoolId()));
        return CommonResult.data(schoolService.listByIds(schoolIds));
    }

    @PostMapping("getRelationClassesList")
    public CommonResult<Collection<String>> getRelationClassesList(@RequestBody PlanQueryParam planQueryParam) {
        List<PlanRelationClasses> planRelationClassesList = planRelationClassesService.listByPlanId(planQueryParam.getPlanId());
        Set<String> classesIds = new HashSet<>();
        planRelationClassesList.forEach(item -> classesIds.add("classes_" + item.getClassesId().toString()));
        return CommonResult.data(classesIds);
    }

    @PostMapping("getGradeClassesList")
    public CommonResult<List<TreeModel>> getGradeClassesList(@RequestBody PlanQueryParam planQueryParam) {
        List<PlanRelationClasses> planRelationClassesList = planRelationClassesService.listByPlanIdAndSchoolId(planQueryParam.getPlanId(), planQueryParam.getSchoolId());

        Set<Integer> classesIds = new HashSet<>();
        planRelationClassesList.forEach(planRelationClasses -> {
            classesIds.add(planRelationClasses.getClassesId());
        });

        List<Classes> classesList = classesService.listByIds(classesIds);
        Map<Integer, List<Classes>> gradeClassesMap = classesList.stream().collect(Collectors.groupingBy(Classes::getGradeId));
        Map<Integer, String> gradeIdMap = new HashMap<>();
        List<TreeModel> gradeTreeModelList = new ArrayList<>();
        classesList.forEach(classes -> {
            if (!gradeIdMap.containsKey(classes.getGradeId())) {
                TreeModel gradeTreeModel = new TreeModel();
                gradeTreeModel.setName(GradeUtil.getGradeName(classes.getGradeId()));
                gradeTreeModel.setId(classes.getGradeId().toString());
                List<Classes> gradeClasses = gradeClassesMap.getOrDefault(classes.getGradeId(), new ArrayList<>());
                List<TreeModel> classesTreeModelList = new ArrayList<>();
                gradeClasses.forEach(gradeClass -> {
                    TreeModel classTreeModel = new TreeModel();
                    classTreeModel.setName(gradeClass.getName());
                    classTreeModel.setCode(gradeClass.getCode());
                    classTreeModel.setId(gradeClass.getId().toString());
                    classesTreeModelList.add(classTreeModel);
                });
                gradeTreeModel.setChildren(classesTreeModelList);
                gradeTreeModelList.add(gradeTreeModel);
                gradeIdMap.put(classes.getGradeId(), "");
            }
        });


        return CommonResult.data(gradeTreeModelList);
    }

    @OperaLog(title = "导出准考证", businessType = BusinessType.OTHER)
    @PostMapping("listForPrintExamTicket")
    public void listForPrintExamTicket(@RequestBody PlanStudent queryParam, HttpServletResponse response) throws IOException {
        response.reset();
        response.setContentType("application/octet-stream;charset=UTF-8");
        String filename = System.currentTimeMillis() + ".zip";
        response.addHeader("Content-Disposition", "inline; filename=" + URLUtil.encode(filename, CharsetUtil.CHARSET_UTF_8));

        Plan plan = planService.getById(queryParam.getPlanId());
        List<PlanStudent> studentList = planStudentService.listByParam(queryParam);
        buildRecords(studentList);

        Map<String, List<PlanStudent>> studentMap = studentList.stream().collect(Collectors.groupingBy(PlanStudent::getSchoolName));

        List<File> files = new ArrayList<>();

        Flowable.fromIterable(studentMap.values())
                .parallel()
                .runOn(Schedulers.computation())
                .flatMap(students -> Flowable.fromCallable(() -> generateExamTicketFile(students)).subscribeOn(Schedulers.io()))
                .sequential()
                .blockingSubscribe(files::add,
                        error -> log.error("并行计算报错", error),
                        () -> {
                            File targetZipFile = new File(plan.getName() + "准考证.zip");
                            generateZip(targetZipFile, files, false);
                            OutputStream out = response.getOutputStream();
                            FileChannel fileChannel = new RandomAccessFile(targetZipFile, "r").getChannel();
                            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                            WritableByteChannel writableByteChannel = Channels.newChannel(out);
                            while (fileChannel.read(byteBuffer) != -1) {
                                byteBuffer.flip();
                                while (byteBuffer.hasRemaining()) {
                                    writableByteChannel.write(byteBuffer);
                                }
                                byteBuffer.clear();
                            }
                            fileChannel.close();
                            writableByteChannel.close();
                            out.close();
                            if (targetZipFile.exists()) {
                                targetZipFile.delete();
                            }
                            if (!CollectionUtils.isEmpty(files)) {
                                files.forEach(File::delete);
                            }
                        }
                );
    }

    public File generateExamTicketFile(List<PlanStudent> students) {

        String schoolName = students.get(0).getSchoolName();

        Map<String, Object> resultMap = new HashMap<>();
        List<String> htmlStrList = new ArrayList<>();

        int startIndex = 0;
        int endIndex;

        //limit标识，每次截取6个打印准考证
        int limit = 6;
        int size = students.size();

        //每次截取的开始位置小于总长度，才会继续执行
        while (startIndex < size) {
            //尾部的位置
            endIndex = startIndex + limit;

            //尾部位置不能超出范围，否则就取集合的长度
            endIndex = Math.min(endIndex, size);

            //截取
            List<PlanStudent> subList = students.subList(startIndex, endIndex);
            subList.forEach(item -> {
                item.setQrCode(Base64.encode(QRCodeUtil.createQrCode(item.getStudyCode())));
            });
            resultMap.put("data", subList);
            try {
                String html = FreemarkerUtil.loadTemplateFromFile("exam_ticket.ftl", resultMap);
                htmlStrList.add(html);
            } catch (IOException | TemplateException e) {
                throw new RuntimeException(e);
            }

            //计算下次截取的开始位置
            startIndex = endIndex;
        }

        byte[] pdfOut = PdfUtil.html2Pdf(htmlStrList);

        File pdfFile = byte2Pdf(pdfOut, schoolName + "准考证表.pdf");
        return pdfFile;
    }

    public static void generateZip(File targetZipFile, List<File> sourceFiles, boolean dirWithFlag) {
        if (CollUtil.isNotEmpty(sourceFiles)) {
            File[] fileArr = sourceFiles.toArray(new File[]{});
            ZipUtil.zip(targetZipFile, dirWithFlag, fileArr);
        }
    }

    public File byte2Pdf(byte[] pdfOut, String name) {
        File file = new File(name);
        try (FileOutputStream fos = new FileOutputStream(file)) {
            fos.write(pdfOut);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

    @PostMapping("pageStudent")
    public CommonResult<Page<PlanStudent>> getPageStudent(@RequestBody CommonPage<PlanStudent> pageParam) {
        Page<PlanStudent> studentPage = planStudentService.getPageStudent(pageParam);
        buildRecords(studentPage.getRecords());
        return CommonResult.data(studentPage);
    }

    private void buildRecords(List<PlanStudent> records) {
        if (!CollectionUtils.isEmpty(records)) {
            Integer planId = records.get(0).getPlanId();
            Plan plan = planService.getById(planId);
            Set<String> studyCodes = new HashSet<>();
            records.forEach(rec -> {
                studyCodes.add(rec.getStudyCode());
            });

            if (plan.getExamFlag()) {
                List<PlanApplyProject> projects = applyProjectService.queryByPlanIdAndStudyCodes(planId, studyCodes);
                Map<String, PlanApplyProject> projectMap = projects.stream().collect(Collectors.toMap(PlanApplyProject::getStudyCode, p -> p));

                records.forEach(rec -> {
                    PlanApplyProject planApplyProject = projectMap.getOrDefault(rec.getStudyCode(), null);
                    if (planApplyProject != null) {
                        rec.setTestPrjNames(Arrays.asList(planApplyProject.getProjectNames().split(",")));
                    }
                });
            } else {
                records.forEach(rec -> {
                    String prjNames = projectConfig.getProjectMap().getOrDefault(rec.getGradeId() + "_" + rec.getSex(), "");
                    if (StringUtils.hasLength(prjNames)) {
                        rec.setTestPrjNames(Arrays.asList(prjNames.split(",")));
                    }
                });
            }
        }
    }

    @PostMapping("batchUpdateStudentData")
    public CommonResult<Boolean> batchUpdateStudentData(@RequestBody PlanQueryParam planQueryParam) {
        boolean isOk = planStudentService.batchUpdateStudentData(planQueryParam.getPlanId());
        if (isOk) {
            CompletableFuture.runAsync(() ->{
                planStudentService.updateStudentInfo(planQueryParam.getPlanId());
                testResultService.updateStudentInfo(planQueryParam.getPlanId());
                summaryResultService.updateStudentInfo(planQueryParam.getPlanId());
                applyProjectService.updateStudentInfo(planQueryParam.getPlanId());
                calculatePlanData(planQueryParam.getPlanId());
            });
        }
        return CommonResult.data(isOk);
    }

    /**
     * 同步新建班级学生数据
     */
    @PostMapping("syncNewClassesStudentData")
    public CommonResult<Integer> syncNewClassesStudentData(@RequestBody PlanQueryParam planQueryParam) {

        if (CollectionUtils.isEmpty(planQueryParam.getClassesIds())) {
            throw new CommonException("请选择具体的年级/班级");
        }
        Set<Integer> classesIdList = new HashSet<>();
        planQueryParam.getClassesIds().forEach(item -> classesIdList.add(Integer.valueOf(item.replaceAll("classes_", ""))));
        List<Classes> classesList = classesService.listByIds(classesIdList);
        List<PlanRelationClasses> planRelationClassesList = planRelationClassesService.listByPlanIdAndClassesIds(planQueryParam.getPlanId(), classesIdList);
        Set<Integer> queryClassesIdList = planRelationClassesList.stream().map(PlanRelationClasses::getClassesId).collect(Collectors.toSet());
        Set<String> classesNames = new HashSet<>();
        classesList.forEach(classes -> {
            if (!queryClassesIdList.contains(classes.getId())) {
                PlanRelationClasses relationClasses = new PlanRelationClasses();
                relationClasses.setPlanId(planQueryParam.getPlanId());
                relationClasses.setShcoolId(classes.getSchoolId());
                relationClasses.setGradeId(classes.getGradeId());
                relationClasses.setClassesId(classes.getId());
                planRelationClassesService.save(relationClasses);
            }
            classesNames.add(classes.getName().trim());
        });
        List<Student> studentList = studentService.listByClassesIds(classesIdList);
        List<PlanStudent> planStudents = planStudentService.listByPlanIdAndClassesNames(planQueryParam.getPlanId(), classesNames);
        List<String> noInsertStudyCodes = planStudents.stream().map(PlanStudent::getStudyCode).collect(Collectors.toList());
        List<Student> waitInsertStudentList = studentList.stream().filter(student -> !noInsertStudyCodes.contains(student.getStudyCode())).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(waitInsertStudentList)) {
            return CommonResult.data(0);
        }

        List<PlanStudent> planStudentList = new ArrayList<>();
        waitInsertStudentList.forEach(student -> {
            PlanStudent planStudent = new PlanStudent();
            BeanUtils.copyProperties(student, planStudent);
            planStudent.setId(null);
            planStudent.setPlanId(planQueryParam.getPlanId());
            planStudent.setSchoolId(planQueryParam.getSchoolId());
            planStudentList.add(planStudent);
        });

        boolean saveOk = planStudentService.saveBatch(planStudentList);

        if (saveOk) {
            Flowable.just(planQueryParam.getPlanId()).subscribe(this::calculatePlanData);
        }

        return CommonResult.data(planStudentList.size());
    }

    @OperaLog(title = "同步新增学生", businessType = BusinessType.INSERT)
    @PostMapping("syncInsertStudentData")
    public CommonResult<Boolean> syncInsertStudentData(@RequestBody PlanQueryParam planQueryParam) {
        List<PlanRelationClasses> planRelationClassesList = planRelationClassesService.listByPlanIdAndSchoolId(planQueryParam.getPlanId(), planQueryParam.getSchoolId());
        Set<Integer> classesIds = planRelationClassesList.stream().map(PlanRelationClasses::getClassesId).collect(Collectors.toSet());
        List<Student> studentList = studentService.listByClassesIds(classesIds);
        List<PlanStudent> planStudents = planStudentService.listByPlanIdAndSchoolId(planQueryParam.getPlanId(), planQueryParam.getSchoolId());
        List<String> noInsertStudyCodes = planStudents.stream().map(PlanStudent::getStudyCode).collect(Collectors.toList());
        List<Student> waitInsertStudentList = studentList.stream().filter(student -> !noInsertStudyCodes.contains(student.getStudyCode())).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(waitInsertStudentList)) {
            return CommonResult.data(true);
        }

        List<PlanStudent> planStudentList = new ArrayList<>();
        waitInsertStudentList.forEach(student -> {
            PlanStudent planStudent = new PlanStudent();
            BeanUtils.copyProperties(student, planStudent);
            planStudent.setId(null);
            planStudent.setPlanId(planQueryParam.getPlanId());
            planStudent.setSchoolId(planQueryParam.getSchoolId());
            planStudentList.add(planStudent);
        });

        boolean saveOk = planStudentService.saveBatch(planStudentList, 500);

        if (saveOk) {
            Flowable.just(planQueryParam.getPlanId()).subscribe(this::calculatePlanData);
        }

        return CommonResult.data(saveOk);
    }


    private void calculatePlanData(Integer planId) {
        Plan plan = planService.getById(planId);
        List<PlanRelationClasses> planRelationClassesList = planRelationClassesService.listByPlanId(planId);
        List<PlanStudent> planStudentList = planStudentService.listByPlanId(planId);

        Map<Integer, List<PlanRelationClasses>> planRelationClassMap;
        Map<Integer, List<PlanStudent>> planStudentMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(planRelationClassesList)) {
            planRelationClassMap = planRelationClassesList.stream().collect(Collectors.groupingBy(PlanRelationClasses::getPlanId));
        } else {
            planRelationClassMap = new HashMap<>();
        }
        if (!CollectionUtils.isEmpty(planStudentList)) {
            planStudentMap = planStudentList.stream().collect(Collectors.groupingBy(PlanStudent::getPlanId));
        }

        Map<Integer, List<PlanStudent>> finalPlanStudentMap = planStudentMap;

        List<PlanRelationClasses> planRelationClasses = planRelationClassMap.getOrDefault(plan.getId(), new ArrayList<>());
        Map<Integer, List<PlanRelationClasses>> schoolMap = planRelationClasses.stream().collect(Collectors.groupingBy(PlanRelationClasses::getShcoolId));
//        plan.setPlanRelationClassesList(planRelationClasses);
        plan.setStudentNum(finalPlanStudentMap.getOrDefault(plan.getId(), new ArrayList<>()).size());
        plan.setSchoolNum(schoolMap.size());
        planService.updateById(plan);
    }

    @OperaLog(title = "新增缓考计划", businessType = BusinessType.INSERT)
    @PostMapping("saveResit")
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Boolean> saveResit(@RequestBody Plan plan) {
        Plan parentPlan = planService.getById(plan.getParentId());
        Plan resitPlan = new Plan();
        BeanUtils.copyProperties(parentPlan, resitPlan);
        resitPlan.setId(null);
        resitPlan.setCode(df.format(LocalDateTime.now()));
        resitPlan.setName(plan.getName());
        resitPlan.setStartDate(plan.getStartDate());
        resitPlan.setEndDate(plan.getEndDate());
        resitPlan.setExamFlag(true);
        resitPlan.setParentId(plan.getParentId());
        resitPlan.setStudentNum(plan.getStudyCodes().size());
        resitPlan.setCreateUserId(Integer.valueOf(StpUtil.getLoginId().toString()));
        planService.save(resitPlan);

        if (!CollectionUtils.isEmpty(plan.getStudyCodes())) {
            List<PlanStudent> planStudentList = planStudentService.queryByPlanIdAndStudyCodes(plan.getParentId(), plan.getStudyCodes());
            if (!CollectionUtils.isEmpty(planStudentList)) {
                List<PlanStudent> planStudents = new ArrayList<>();
                planStudentList.forEach(student -> {
                    PlanStudent planStudent = new PlanStudent();
                    BeanUtils.copyProperties(student, planStudent);
                    planStudent.setId(null);
                    planStudent.setPlanId(resitPlan.getId());
                    planStudents.add(planStudent);
                });
                List<PlanRelationClasses> planRelationClassesList = new ArrayList<>();
                planStudentList.forEach(planStudent -> {
                    PlanRelationClasses planRelationClasses = new PlanRelationClasses();
                    planRelationClasses.setPlanId(resitPlan.getId());
                    planRelationClasses.setGradeId(planStudent.getGradeId());
                    planRelationClasses.setShcoolId(planStudent.getSchoolId());
                    planRelationClasses.setClassesId(planStudent.getId());
                    planRelationClassesList.add(planRelationClasses);
                });
                Map<Integer, List<PlanRelationClasses>> planRelationMap = planRelationClassesList.stream().collect(Collectors.groupingBy(PlanRelationClasses::getGradeId));
                planRelationMap.forEach((gradeId, relations) -> {
                    CompletableFuture.runAsync(() -> {
                        planRelationClassesService.saveOrUpdateBatch(relations);
                        calculatePlanData(resitPlan.getId());
                    });
                });

                planStudentService.saveOrUpdateBatch(planStudents);
            }
        }

        return CommonResult.data(true);
    }

    @OperaLog(title = "新增计划", businessType = BusinessType.INSERT)
    @PostMapping("save")
    public CommonResult<Boolean> onSave(@RequestBody Plan plan) {
        if (plan.getId() == null) {
            Plan planByName = planService.getByName(plan.getName());
            if (planByName != null) {
                throw new CommonException("计划名称已存在，无法添加！");
            }
            if (CollectionUtils.isEmpty(plan.getSelectClassesList())) {
                throw new CommonException("请选择关联体测对象！");
            }
            List<Integer> classesIds = new ArrayList<>();
            plan.getSelectClassesList().forEach(item -> {
                if (item.contains("classes_")) {
                    String classesId = item.replaceAll("classes_", "");
                    classesIds.add(Integer.valueOf(classesId));
                }
            });
            if (CollectionUtils.isEmpty(classesIds)) {
                throw new CommonException("选择的学校下无班级数据");
            }
            plan.setCreateTime(LocalDateTime.now());
            plan.setCode(df.format(LocalDateTime.now()));
            plan.setState(1);
            plan.setCreateUserId(Integer.valueOf(StpUtil.getLoginId().toString()));
            planService.saveOrUpdate(plan);

            List<Classes> classesList = classesService.listByIds(classesIds);
            List<Student> studentList = studentService.listByClassesIds(classesIds);
//                if(CollectionUtils.isEmpty(studentList)){
//                    throw new CommonException("您选择的班级下无学生数据");
//                }
            List<PlanRelationClasses> planRelationClassesList = new ArrayList<>();
            classesList.forEach(classes -> {
                PlanRelationClasses planRelationClasses = new PlanRelationClasses();
                planRelationClasses.setPlanId(plan.getId());
                planRelationClasses.setGradeId(classes.getGradeId());
                planRelationClasses.setShcoolId(classes.getSchoolId());
                planRelationClasses.setClassesId(classes.getId());
                planRelationClassesList.add(planRelationClasses);
            });
            List<PlanStudent> planStudentList = new ArrayList<>();
            studentList.forEach(student -> {
                PlanStudent planStudent = new PlanStudent();
                BeanUtils.copyProperties(student, planStudent);
                planStudent.setId(null);
                planStudent.setPlanId(plan.getId());
                planStudentList.add(planStudent);
            });
            if (!CollectionUtils.isEmpty(planRelationClassesList)) {
                Map<Integer, List<PlanRelationClasses>> planRelationMap = planRelationClassesList.stream().collect(Collectors.groupingBy(PlanRelationClasses::getGradeId));
                planRelationMap.forEach((gradeId, relations) -> {
                    CompletableFuture.runAsync(() -> {
                        planRelationClassesService.saveOrUpdateBatch(relations);
                        calculatePlanData(plan.getId());
                    });
                });
            }

            if (!CollectionUtils.isEmpty(planStudentList)) {
                Map<Integer, List<PlanStudent>> planStudentMap = planStudentList.stream().collect(Collectors.groupingBy(PlanStudent::getGradeId));
                planStudentMap.forEach((gradeId, stds) -> {
                    CompletableFuture.runAsync(() -> {
                        planStudentService.saveOrUpdateBatch(stds);
                        calculatePlanData(plan.getId());
                    });
                });
            }


        } else {
            Plan oldPlan = planService.getById(plan.getId());
            if (!oldPlan.getName().equals(plan.getName())) {
                Plan planByName = planService.getByName(plan.getName());
                if (planByName != null) {
                    throw new CommonException("计划名称已存在，无法添加！");
                }
            }
            planService.saveOrUpdate(plan);
        }

        return CommonResult.data(true);
    }


    private final static DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    @Operation(summary = "生成上报成绩excel")
    @GetMapping("/downloadReportFile")
    public void downloadReportFile(PlanQueryParam planQueryParam, HttpServletResponse response) throws IOException {
        if (planQueryParam.getPlanId() == null) {
            throw new CommonException("请先选择体测计划");
        }

        Plan testPlan = planService.getById(planQueryParam.getPlanId());
        School school = schoolService.getById(planQueryParam.getSchoolId());
        List<TestResult> testResultList = testResultService.listByPlanParam(planQueryParam);
        List<SysHealthProject> healthProjects = healthProjectService.listByTemplateId(testPlan.getTemplateId());
        Map<String, SysHealthProject> projectMap = healthProjects.stream().collect(Collectors.toMap(t -> t.getProjectName() + "_" + t.getGradeId(), p -> p));
        if (!CollectionUtils.isEmpty(testResultList)) {
            Map<String, List<TestResult>> testResultMap = testResultList.stream().collect(Collectors.groupingBy(TestResult::getStudyCode));
            Set<String> studyCodes = new HashSet<>();
            testResultList.forEach(testResult -> studyCodes.add(testResult.getStudyCode()));
            List<PlanStudent> studentList = planStudentService.queryByPlanIdAndStudyCodes(testPlan.getId(), studyCodes);
            Map<String, PlanStudent> studentMap = studentList.stream().collect(Collectors.toMap(PlanStudent::getStudyCode, s -> s));
            List<ExportClassesModel> classesModels = new ArrayList<>();
            List<ExportTestResultModel> psTestResultModels = new ArrayList<>();
            List<ExportTestResultModel> hsTestResultModels = new ArrayList<>();
            List<ExportStudentModel> studentModels = new ArrayList<>();
            List<ExportTestEnvModel> testEnvModels = new ArrayList<>();
            Map<String, String> classesMap = new HashMap<>();
            Map<String, String> testEnvMap = new HashMap<>();
            testResultMap.forEach((studyCode, testResultsList) -> {
                PlanStudent student = studentMap.getOrDefault(studyCode, null);
                if (student != null) {
                    ExportStudentModel exportStudentModel = new ExportStudentModel();
                    exportStudentModel.setStudyCode(studyCode);
                    exportStudentModel.setAddress(student.getAddress());
                    if (student.getBirthday() != null) {
                        exportStudentModel.setBirthday(DATE_FORMATTER.format(student.getBirthday()));
                    }
                    exportStudentModel.setGradeId(String.valueOf(student.getGradeId()));
                    exportStudentModel.setClassCode(student.getClassesCode());
                    exportStudentModel.setFromSource(student.getFromSource());
                    exportStudentModel.setClassName(student.getClassesName());
                    exportStudentModel.setNation(student.getNation());
                    exportStudentModel.setName(student.getName());
                    exportStudentModel.setSex(student.getSex());
                    exportStudentModel.setIdNumber(student.getIdNumber());
                    studentModels.add(exportStudentModel);


                    ExportTestResultModel exportTestResultModel = new ExportTestResultModel();
                    BeanUtils.copyProperties(exportStudentModel, exportTestResultModel);
                    Map<String, List<TestResult>> stdTestResultPrjMap = testResultsList.stream().collect(Collectors.groupingBy(TestResult::getPrjCode));
                    stdTestResultPrjMap.forEach((prjCode, stdTestResultList) -> {
                        ExportTestEnvModel testEnvModel = new ExportTestEnvModel();
                        testEnvModel.setGradeId(String.valueOf(student.getGradeId()));
                        testEnvModel.setClassCode(student.getClassesCode());
                        testEnvModel.setClassName(student.getClassesName());
                        testEnvModel.setTestAddress("田径场");
                        testEnvModel.setTestType("仪器");
                        testEnvModel.setTestTeacherName("某老师");
                        testEnvModel.setTestTime(DATE_FORMATTER.format(LocalDate.now()));
                        testEnvModel.setPrjName(ProjectUtil.getPrjName(prjCode));
                        testEnvModel.setTestBotName(ProjectUtil.getPrjName(prjCode) + "测试仪");
                        if (!testEnvMap.containsKey(student.getGradeId() + "_" + student.getClassesCode() + "_" + prjCode)) {
                            testEnvMap.put(student.getGradeId() + "_" + student.getClassesCode() + "_" + prjCode, prjCode);
                            testEnvModels.add(testEnvModel);
                        }
                    });
                    List<TestResult> heightResults = stdTestResultPrjMap.getOrDefault("2", null);
                    List<TestResult> weightResults = stdTestResultPrjMap.getOrDefault("3", null);
                    List<TestResult> vitalResults = stdTestResultPrjMap.getOrDefault("1", null);
                    List<TestResult> run50Results = stdTestResultPrjMap.getOrDefault("7", null);
                    List<TestResult> sitAndReachResults = stdTestResultPrjMap.getOrDefault("5", null);
                    List<TestResult> ropeSkipResults = stdTestResultPrjMap.getOrDefault("8", null);
                    List<TestResult> abdominalResults = stdTestResultPrjMap.getOrDefault("9", null);
                    List<TestResult> run508Results = stdTestResultPrjMap.getOrDefault("15", null);

                    //立定跳远
                    List<TestResult> jumpResults = stdTestResultPrjMap.getOrDefault("6", null);
                    List<TestResult> pullUpResults = stdTestResultPrjMap.getOrDefault("10", null);
                    List<TestResult> run800Results = stdTestResultPrjMap.getOrDefault("11", null);
                    List<TestResult> run1000Results = stdTestResultPrjMap.getOrDefault("12", null);

                    String heightResult = CollectionUtils.isEmpty(heightResults) ? "" : heightResults.get(0).getResult();
                    String weightResult = CollectionUtils.isEmpty(weightResults) ? "" : weightResults.get(0).getResult();
                    String vitalResult = CollectionUtils.isEmpty(vitalResults) ? "" : vitalResults.get(0).getResult();
                    String run50Result = CollectionUtils.isEmpty(run50Results) ? "" : run50Results.get(0).getResult();
                    String sitAndReachResult = CollectionUtils.isEmpty(sitAndReachResults) ? "" : sitAndReachResults.get(0).getResult();
                    String ropeSkipResult = CollectionUtils.isEmpty(ropeSkipResults) ? "" : ropeSkipResults.get(0).getResult();
                    String abdominalResult = CollectionUtils.isEmpty(abdominalResults) ? "" : abdominalResults.get(0).getResult();
                    String run508Result = CollectionUtils.isEmpty(run508Results) ? "" : run508Results.get(0).getResult();
                    String jumpResult = CollectionUtils.isEmpty(jumpResults) ? "" : jumpResults.get(0).getResult();
                    String pullUpResult = CollectionUtils.isEmpty(pullUpResults) ? "" : pullUpResults.get(0).getResult();
                    String run800Result = CollectionUtils.isEmpty(run800Results) ? "" : run800Results.get(0).getResult();
                    String run1000Result = CollectionUtils.isEmpty(run1000Results) ? "" : run1000Results.get(0).getResult();

                    exportTestResultModel.setHeightResult(heightResult);
                    exportTestResultModel.setWeightResult(weightResult);
                    exportTestResultModel.setVitalResult(vitalResult);
                    exportTestResultModel.setRun50Result(ProcessTimeResultUtil.processRunResult(run50Result, "秒", planQueryParam.getExportTimeType(),"50米跑"));
                    exportTestResultModel.setSitAndReachResult(sitAndReachResult);
                    exportTestResultModel.setRopeSkipResult(ropeSkipResult);
                    exportTestResultModel.setAbdominalResult(abdominalResult);
                    exportTestResultModel.setRun508Result(ProcessTimeResultUtil.processRunResult(run508Result, "分.秒", planQueryParam.getExportTimeType(),"50米×8往返跑"));
                    exportTestResultModel.setLdtyResult(jumpResult);
                    exportTestResultModel.setYtxsResult(pullUpResult);
                    exportTestResultModel.setRun800Result(ProcessTimeResultUtil.processRunResult(run800Result, "分.秒", planQueryParam.getExportTimeType(),"800米"));
                    exportTestResultModel.setRun1000Result(ProcessTimeResultUtil.processRunResult(run1000Result, "分.秒", planQueryParam.getExportTimeType(),"1000米"));

                    if (student.getGradeId() < 20) {
                        psTestResultModels.add(exportTestResultModel);
                    } else {
                        hsTestResultModels.add(exportTestResultModel);
                    }


                    if (!classesMap.containsKey(student.getClassesCode())) {
                        ExportClassesModel exportClassesModel = new ExportClassesModel();
                        exportClassesModel.setClassCode(student.getClassesCode());
                        exportClassesModel.setClassName(student.getClassesName());
                        exportClassesModel.setGradeId(String.valueOf(student.getGradeId()));
                        classesModels.add(exportClassesModel);
                        classesMap.put(student.getClassesCode(), student.getClassesName());
                    }
                }
            });

            ClassPathResource classesPathResource = new ClassPathResource("excel/班级基本信息模板.xlsx");
            ClassPathResource studentPathResource = new ClassPathResource("excel/学生基本信息模板.xlsx");
            ClassPathResource testResultPathResource = new ClassPathResource("excel/体测成绩模板.xlsx");
            ClassPathResource hsTestResultPathResource = new ClassPathResource("excel/高校体测成绩模板.xlsx");
            ClassPathResource testEnvPathResource = new ClassPathResource("excel/测试环境信息模板.xlsx");

            File classesFile = new File("班级基本信息.xlsx");
            File studentFile = new File("学生基本信息.xlsx");
            File testResultFile = new File("小学体测成绩.xlsx");
            File hsTestResultFile = new File("高校体测成绩.xlsx");
            File testEnvFile = new File("测试环境信息.xlsx");

            List<File> fileList = new ArrayList<>();

            if (!classesFile.exists()) {
                boolean isOk = classesFile.createNewFile();
                if (isOk) {
                    fileList.add(classesFile);
                }
            }
            if (!studentFile.exists()) {
                boolean isOk = studentFile.createNewFile();
                if (isOk) {
                    fileList.add(studentFile);
                }
            }
            if (!testResultFile.exists()) {
                boolean isOk = testResultFile.createNewFile();
                if (isOk) {
                    if (school.getSchoolType().contains("1")) {
                        fileList.add(testResultFile);
                    }
                }
            }
            if (!testEnvFile.exists()) {
                boolean isOk = testEnvFile.createNewFile();
                if (isOk) {
                    fileList.add(testEnvFile);
                }
            }
            if (!hsTestResultFile.exists()) {
                boolean isOk = hsTestResultFile.createNewFile();
                if (isOk) {
                    if (school.getSchoolType().contains("2") || school.getSchoolType().contains("3") || school.getSchoolType().contains("4")) {
                        fileList.add(hsTestResultFile);
                    }
                }
            }
            try {
                ExcelWriter excelWriter = EasyExcel.write(classesFile).withTemplate(classesPathResource.getInputStream()).build();
                WriteSheet writeSheet = EasyExcel.writerSheet().build();
                List<ExportClassesModel> exportClassesModelList = classesModels.stream().sorted(Comparator.comparing(ExportClassesModel::getGradeId).thenComparing(ExportClassesModel::getClassCode)).collect(Collectors.toList());
                excelWriter.fill(exportClassesModelList, writeSheet);
                excelWriter.finish();


                ExcelWriter excelStudentWriter = EasyExcel.write(studentFile).withTemplate(studentPathResource.getInputStream()).build();
                WriteSheet writeStudentSheet = EasyExcel.writerSheet().build();
                List<ExportStudentModel> studentResults = studentModels.stream().sorted(Comparator.comparing(ExportStudentModel::getGradeId).thenComparing(ExportStudentModel::getClassCode)).collect(Collectors.toList());
                excelStudentWriter.fill(studentResults, writeStudentSheet);
                excelStudentWriter.finish();


                ExcelWriter excelTestResultWriter = EasyExcel.write(testResultFile).withTemplate(testResultPathResource.getInputStream()).build();
                WriteSheet writeTestResultSheet = EasyExcel.writerSheet().build();
                List<ExportTestResultModel> psResults = psTestResultModels.stream().sorted(Comparator.comparing(ExportTestResultModel::getGradeId).thenComparing(ExportTestResultModel::getClassCode)).collect(Collectors.toList());
                excelTestResultWriter.fill(new FillWrapper("ps", psResults), writeTestResultSheet);
                excelTestResultWriter.finish();


                ExcelWriter highSchoolExcelTestResultWriter = EasyExcel.write(hsTestResultFile).withTemplate(hsTestResultPathResource.getInputStream()).build();
                WriteSheet highSchoolWriteTestResultSheet = EasyExcel.writerSheet().build();
                List<ExportTestResultModel> hsResults = hsTestResultModels.stream().sorted(Comparator.comparing(ExportTestResultModel::getGradeId).thenComparing(ExportTestResultModel::getClassCode)).collect(Collectors.toList());
                highSchoolExcelTestResultWriter.fill(new FillWrapper("hs", hsResults), highSchoolWriteTestResultSheet);
                highSchoolExcelTestResultWriter.finish();


                ExcelWriter excelEnvWriter = EasyExcel.write(testEnvFile).withTemplate(testEnvPathResource.getInputStream()).build();
                WriteSheet writeEnvSheet = EasyExcel.writerSheet().build();
                List<ExportTestEnvModel> testEnvResults = testEnvModels.stream().filter(item -> !item.getPrjName().equalsIgnoreCase("BMI")).sorted(Comparator.comparing(ExportTestEnvModel::getGradeId).thenComparing(ExportTestEnvModel::getClassCode)).collect(Collectors.toList());
                excelEnvWriter.fill(testEnvResults, writeEnvSheet);
                excelEnvWriter.finish();

                String fileName = URLEncoder.encode("【" + school.getName() + "】体测上报成绩数据.zip", "UTF-8").replaceAll("\\+", "%20");

                File zipFile = new File(uploadPath + "/" + fileName);

                File finalZipFile = ZipUtil.zip(zipFile, false, fileList.toArray(new File[0]));

                testResultFile.delete();
                studentFile.delete();
                classesFile.delete();
                testEnvFile.delete();
                hsTestResultFile.delete();

                FileInputStream fileInputStream = new FileInputStream(finalZipFile);

                ServletOutputStream outputStream = response.getOutputStream();
                byte[] bytes = new byte[1024 * 10];
                int read;
                do {
                    read = fileInputStream.read(bytes);
                    outputStream.write(bytes, 0, read);
                } while (-1 != read);

                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/octet-stream");
                response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
                response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");

                IOUtils.closeQuietly(fileInputStream);
                IOUtils.closeQuietly(outputStream);
                finalZipFile.delete();
            } catch (Exception e) {
                log.error("下载文件", e);
            }
        }
    }
}
